
package org.zmhhxl.samples3.resource.gateway.configure;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter;
import org.springframework.security.oauth2.server.resource.web.access.server.BearerTokenServerAccessDeniedHandler;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.authentication.RedirectServerAuthenticationEntryPoint;
import reactor.core.publisher.Mono;

import java.util.HashSet;

@Configuration
@EnableWebFluxSecurity
//@EnableReactiveMethodSecurity
@Slf4j
public class ResourceServerConfig {


   @Value("${spring.security.permitAllPaths: favicon.ico,/login/**,/test/**,/resources/**, /error,/webjars/**}")
   private String[] permitAllPaths;

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
       // 禁用csrf与cors
       http.csrf(ServerHttpSecurity.CsrfSpec::disable);
       //http.cors(ServerHttpSecurity.CorsSpec::disable);

        http
              //.securityMatcher(exchange -> exchange.addUrlTransformer()).anonymous()
              .authorizeExchange(authorizeExchange ->
                    authorizeExchange
                          //放行静态资源
                          //.requestMatchers("/assets/**", "/webjars/**", "/login", "/getCaptcha", "/getSmsCaptcha").permitAll()
                          // 使用动态路径匹配，提高灵活性
                          .pathMatchers(HttpMethod.OPTIONS, "/auth/.well-known/*").permitAll()  // 放行的操作 "/**" openid-configuration
                          .pathMatchers(HttpMethod.OPTIONS, "/auth/oauth2/jwks").permitAll()  // "/**"
                          .pathMatchers(HttpMethod.OPTIONS, "/auth/oauth2/authorize").permitAll()  // "/**"
                          .pathMatchers(HttpMethod.OPTIONS, "/auth/session-check").permitAll()
                          .pathMatchers(HttpMethod.OPTIONS, "/auth/userinfo").permitAll()
                          .pathMatchers(HttpMethod.OPTIONS, "/auth/oauth2/token").permitAll()

                          .pathMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                          .pathMatchers(permitAllPaths).permitAll()
                          .anyExchange().authenticated()
              )
//              .exceptionHandling(ex -> ex
//                    /**
//                     * 只有以下2种异常会走这里
//                     * */
//                    // 统一处理未认证和访问被拒绝的异常
//                    .authenticationEntryPoint(new RedirectServerAuthenticationEntryPoint("http://127.0.0.1:9000/login"))
//                    .accessDeniedHandler(new BearerTokenServerAccessDeniedHandler())
//              )
//              // 使用oauth2登录
//              .oauth2Login(Customizer.withDefaults())

              /**
               * 配置OAuth2资源服务器，以支持JWT（JSON Web Token）认证。
               *
               * <p>该方法通过指定JWT的自定义配置来设置OAuth2资源服务器的安全约束。它使用了
               * {@link org.springframework.security.config.annotation.web.configurers.OAuth2ResourceServerConfigurer#jwt}
               * 方法来配置JWT处理器，并通过 {@link org.springframework.security.config.annotation.web.configurers.OAuth2ResourceServerConfigurer.JwtConfigurer#jwt}
               * 方法的 {@link org.springframework.security.config.annotation.web.configurers.OAuth2ResourceServerConfigurer.JwtConfigurer#Customizer}
               * 参数来提供默认的JWT配置自定义。
               *
               * 设置当前网关服务作为资源服务，解析请求头中的token
               *
               * @param jwt 用于配置JWT处理器的定制器
               * @return 返回配置了OAuth2资源服务器的Bean
               */
              .oauth2ResourceServer(jwt-> jwt.jwt(Customizer.withDefaults())

                 /*
                // xhr请求未携带Token处理, 发起身份验证流,用于请求身份验证 缺省为new BearerTokenServerAuthenticationEntryPoint
                .authenticationEntryPoint(this::authenticationEntryPoint)
                // 权限不足处理
                .accessDeniedHandler(this::accessDeniedHandler)
                // Token解析失败处理，处理身份验证失败
                .authenticationFailureHandler(this::failureHandler)
                //缺省是ServerBearerTokenAuthenticationConverter.java,将ServerWebExchange转换为认证(String)
                .bearerTokenConverter
                //如果ServerAuthenticationConverter可以将ServerWebExchange转换为认证，则匹配。
                .authenticationConverterServerWebExchangeMatcher
                // 配置JWT资源服务器支持
                .jwt
                //配置不透明令牌资源服务器支持
                .opaqueToken
                //基于提供的上下文解析ReactiveAuthenticationManager的接口
                .authenticationManagerResolver
                */
              )
//              .oauth2ResourceServer(resourceServer -> resourceServer
//                    .jwt(jwt->jwt.jwtAuthenticationConverter(grantedAuthoritiesExtractor())))
              //.jwt(jwt->jwt.jwtDecoder(jwtDecoderFactory.createDecoder()))

        ;

        return http.build();
    }

   /**
    * 专属于流式服务器的jwt解析器
    *
    * 自定义jwt解析器，设置解析出来的权限信息的前缀与在jwt中的key
    *
    * 此配置可用于配置多个资源服务，作为解析jwt的适配器
    *
    * @return jwt解析器适配器 ReactiveJwtAuthenticationConverterAdapter
    */
   private Converter<Jwt, ? extends Mono<? extends AbstractAuthenticationToken>> grantedAuthoritiesExtractor() {
                                                           //new GrantedAuthoritiesExtractor();
      JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
      // 设置解析权限信息的前缀，设置为空是去掉前缀
      grantedAuthoritiesConverter.setAuthorityPrefix("");
      // 设置权限信息在jwt claims中的key
      grantedAuthoritiesConverter.setAuthoritiesClaimName("authorities");

      JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
      jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
      return new ReactiveJwtAuthenticationConverterAdapter(jwtAuthenticationConverter);
   }

}
   // 自定义用户详情服务
//   @Bean
//   public MapReactiveUserDetailsService reactiveUserDetailsService() {
//      UserDetails user = User.withDefaultPasswordEncoder().username("user").password("password").roles("USER").build();
//      return new MapReactiveUserDetailsService(user);
//   }

   // 自定义限流器
//   @Bean
//   RedisRateLimiter redisRateLimiter() {
//      return new RedisRateLimiter(1, 2);
//   }

//   // 自定义未认证的入口点
//   private static class MyServerAuthenticationEntryPoint implements ServerAuthenticationEntryPoint {
//      @Override
//      public Mono<Void> commence(ServerWebExchange exchange, AuthenticationException e) throws IOException, ServletException {
//         // TODO: 实现未认证用户的处理逻辑，例如返回401状态码
//         exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
//         exchange.getResponse().getHeaders().add("WWW-Authenticate", "Bearer error=\"invalid_token\"");
//         exchange.getResponse().setComplete();
//      }
//   }
//
//   // 自定义访问被拒绝处理
//   private static class MyServerAccessDeniedHandler implements ServerAccessDeniedHandler {
//      @Override
//      public Mono<Void> handle(ServerWebExchange exchange, AccessDeniedException exception) throws IOException, ServletException {
//         return (exchange, exception) -> {
//            log.warn("Access denied for user: {}", exception.getAuthentication());
//            log.debug("Access denied details: ", exception);
//
//            // 设置响应状态码为403 Forbidden
//            exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
//
//            // 构建错误消息并写入响应体
//            Map<String, Object> errorDetails = new HashMap<>();
//            errorDetails.put("message", "Access denied");
//            errorDetails.put("cause", exception.getMessage());
//
//            // 使用Jackson库将错误详情转换为JSON并写入响应体
//            ObjectMapper mapper = new ObjectMapper();
//            byte[] errorJson = mapper.writeValueAsBytes(errorDetails);
//            DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(errorJson);
//            exchange.getResponse().getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
//            exchange.getResponse().writeWith(Mono.just(buffer));
//
//            return Mono.empty();
//         };
//      }
//   }

